package edu.stanford.junction.tictactoe;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TextView;
import edu.stanford.junction.android.AndroidJunctionMaker;
import edu.stanford.junction.api.activity.ActivityScript;
import edu.stanford.junction.api.activity.JunctionActor;
import edu.stanford.junction.api.messaging.MessageHeader;
import edu.stanford.junction.provider.xmpp.XMPPSwitchboardConfig;

public class Game extends Activity {
	
	private enum notification {NOT_MY_TURN, CELL_FULL, VICTORY, DEFEAT, STALEMATE};
	private enum TTTAction {PLAY, JOIN_GAME};
	static final int MENU_INVITE_ID = 3;
	static final int MENU_INVITE_TEXT_ID = 4;
	private TextView playerTileText, turnText;
	private Button b00, b01, b02, b10, b11, b12, b20, b21, b22;
	private Button[][] boardButtons = {{b00, b01, b02},{b10, b11, b12},{b20,b21,b22}};
	private TextView statusText;
	private Board board;
	private boolean myTurnFlag;
	private JunctionActor player = null;
	private Handler mainHandler;
	private boolean joined;
	private boolean gameLeader;
	private Player xes, oes, me;
	private boolean childCanDie;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.board);
        initText();
        initBoard();
        //Log.d("Game", "about to makeMe");
        makeMe();
        connectToJunction(savedInstanceState);
    }
    
    private void makeMe() {
    	SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
    	String firstName = prefs.getString("firstNamePref", "Default");
    	String lastName = prefs.getString("lastNamePref", "Player");
    	String avatarURL = prefs.getString("avatarPref", "http://i29.tinypic.com/21c8fi8.jpg"); 
    	
    	if(avatarURL.length() < 1) {
    		avatarURL = "http://i29.tinypic.com/21c8fi8.jpg";
    	}
    	
    	if(!avatarURL.substring(0, 7).equals("http://"))
    		avatarURL = "http://" + avatarURL;
    	
    	me = new Player("", firstName, lastName, avatarURL, Board.empty_slot);
    }

    private void initMainHandler() {
    	mainHandler = new Handler() {
			@Override
			public void handleMessage(Message msgFromChild) {
				JSONObject msg = (JSONObject) msgFromChild.obj;
				//Log.d(PP, "MY ID: " + myId + " Message received: " + msg);
				if(msg.optString("action").length() < 1) return;
				JSONObject data = msg.optJSONObject("data");
				handleAction(msg.optString("action"), data);	
			}
		};
    }
    
    private void makeJunctionPlayer() {
    	if(player == null) {
    		player = new JunctionActor("player") {
    			@Override
    			public void onActivityJoin() {
    				joinGame();
    			}

				@Override
				public void onMessageReceived(MessageHeader header,
						JSONObject msg) {		
					if(childCanDie) return;
					Message toMain = mainHandler.obtainMessage();
			        toMain.obj = msg;
			        mainHandler.sendMessage(toMain);
				}
    		};
    		
    		me.setPlayerId(player.getActorID());
    	}
    }
    
    private ActivityScript makeScript() {
    	ActivityScript desc = new ActivityScript();
    	desc.setFriendlyName("jTicTacToe");
    	desc.setActivityID("edu.stanford.junction.tictactoe");
    	desc.setHost("prpl.stanford.edu");
    	JSONObject platform = new JSONObject();
    	try {
			platform.put("platform", "android");
			platform.put("package", "edu.stanford.junction.tictactoe");
	    	platform.put("url", "http://prpl.stanford.edu/junction/android/jTicTacToe.apk");
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}      	
    	desc.addRolePlatform("player", "android", platform);
		return desc;
    }
    
    private void connectToJunction(Bundle savedInstanceState) {
    	initMainHandler();

		// hack by BJD. If there is a player, we're already in a game.
		if (player != null) return;
    	
    	Log.d("game", "CREATING JUNCTION");
    	if(AndroidJunctionMaker.isJoinable(this)) {
    	//if (false) {
    		XMPPSwitchboardConfig config = new XMPPSwitchboardConfig("prpl.stanford.edu");
    	    AndroidJunctionMaker maker = AndroidJunctionMaker.getInstance(config);
    	    makeJunctionPlayer();
    	      // Join the intent's activity session:
    	    maker.newJunction(this, player);
    	    joined = true;
    	} else {
    		ActivityScript desc = makeScript();
    		makeJunctionPlayer();
    		AndroidJunctionMaker.getInstance(null).newJunction(desc, player);
    		//gameLeader = true;
    		joined = true;
    	}
    		
    }
	
	private void joinGame() {
    	try {
	    	if(joined) return;
	    	JSONObject data = new JSONObject();
			data.put("playerId", me.getPlayerId());
	    	data.put("firstName", me.getFirstName());
	    	data.put("lastName", me.getLastName());
	    	data.put("avatarURL", me.getAvatarURL());
	    	data.put("tile", me.getPlayerTile());
	    	sendMessage("JOIN_GAME", data);
        } catch (JSONException e) {
			e.printStackTrace();
		}
    }
	
    private void registerPlayer(JSONObject data) {
    	if (data.optInt("tile") == Board.player_o) oes = new Player(data, Board.player_o);
    	else xes = new Player(data, Board.player_x);
    }

	private void initText() {
		playerTileText = (TextView) findViewById(R.id.PlayerTileText);
		turnText = (TextView) findViewById(R.id.TurnText);
    }

	private void theirTurn() {
		updateGameStatus();
		myTurnFlag = false;
		turnText.setText(R.string.theirTurn);
	}

	private void myTurn() {
		updateGameStatus();
		myTurnFlag = true;
		turnText.setText(R.string.yourTurn);
	}

	private void initBoard() {
		board = new Board();
		bindBoardButtons();  
		updateBoardDisplay();
	}
	
    private void bindBoardButtons() {
    	TableLayout boardView = (TableLayout) findViewById(R.id.Board);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				boardButtons[i][j] = (Button) boardView.findViewWithTag("b".concat(Integer.toString(i)).concat(Integer.toString(j)));
		    	boardButtons[i][j].setOnClickListener(new View.OnClickListener() {
		    		public void onClick(View v) { boardButtonPushed(v); }
		    	});
			}
		}
	}

    protected void boardButtonPushed(View v) {
		if (!myTurnFlag) showNotification(notification.NOT_MY_TURN);
		else {
			Button curr = (Button) v;
			int rowSel = -1;
			int colSel = -1;
			for (int i = 0; i < 3; i++) {
				for (int j = 0; j < 3; j++) {
					if (curr.equals(boardButtons[i][j])) {
						rowSel = i;
						colSel = j;
					}
				}
			}
			if (rowSel == -1 || colSel == -1) {
				//TODO fatal error
			}
			if (board.getCell(rowSel, colSel) != Board.empty_slot) showNotification(notification.CELL_FULL);
			else {
				setCell(me.getPlayerTile(), rowSel, colSel);
				sendCellChoice(rowSel, colSel);
				theirTurn();
			}
		}
		
	}

	private void setCell(int playerTile, int rowSel, int colSel) {
		board.makeMove(playerTile, rowSel, rowSel);
		updateBoardDisplay();
	}

	private void updateBoardDisplay() {
    	for (int i = 0; i < 3; i++) {
    		for (int j = 0; j < 3; j++) {
    			switch (board.getCell(i, j)) {
    			case Board.player_o: boardButtons[i][j].setText("O"); break;
    			case Board.player_x: boardButtons[i][j].setText("X"); break;
    			case Board.empty_slot: boardButtons[i][j].setText(""); break;
    			}
    		}
    	}
	}
	
	private void sendCellChoice(int rowSel, int colSel) {
		try {
			JSONObject choice = new JSONObject();
			choice.put("row", rowSel);
			choice.put("col", colSel);
			choice.put("player", me.getPlayerTile());
	    	sendMessage("PLAY", choice);
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
	
    private void sendMessage(String action, Object data){
    	try {
	    	JSONObject msg = new JSONObject();
			msg.put("action", action);
			msg.put("data", data);	
	    	player.getJunction().sendMessageToSession(msg);
    	} catch (JSONException e) {
			e.printStackTrace();
		}
    }

    private void handleAction(String action, JSONObject data){
    	switch(TTTAction.valueOf(action)) {
    	case PLAY:
    		setCell(data.optInt("player"), data.optInt("row"), data.optInt("col"));
    		myTurn();
    		break;
    	case JOIN_GAME:
    		registerPlayer(data);
    		break;
    	}
    }
	
	public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_INVITE_ID, 0, "Invite by QR");
        menu.add(0, MENU_INVITE_TEXT_ID, 0, "Invite by SMS");
        return true;
    }

    /* Handles item selections */
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_INVITE_ID:
            invite();
            return true;
        case MENU_INVITE_TEXT_ID:
            inviteBySMS();
            return true;
        }
        return false;
    }

   
    private void invite() {
    	AndroidJunctionMaker.getInstance(null).inviteActorByQR(this, player.getJunction(), "player");
    }
    
    private void inviteBySMS() {
    	Intent sms = new Intent("junction.intent.action.invite.TEXT");
    	String uri = "junction://prpl.stanford.edu/";
    	uri += player.getJunction().getSessionID();
    	uri += "?requestedRole=player";
    	sms.putExtra("invitation", uri);
    	Log.d("HI", "ADDED EXTRAS: " + uri);
    	startActivity(sms);
    }

	private void showNotification(notification n) {
		switch (n) {
		case NOT_MY_TURN: statusText.setText(R.string.notYourTurn);
		case CELL_FULL: statusText.setText(R.string.cellFull);
		}		
	}
    
	private void updateGameStatus() {
		if (board.checkGameStatus() == Board.empty_slot) return;
		if (board.checkGameStatus() == me.getPlayerTile()) endGame(notification.VICTORY);
		else if (board.checkGameStatus() == Board.stalemate) endGame(notification.STALEMATE);
		else endGame(notification.DEFEAT);		
	}

	private void endGame(notification n) {
		//TODO close down the board/junction? what?
		//showEndGameMessage(n, endingText);
	}
	
	private void showEndGameMessage(notification n, TextView tv) {
		switch (n) {
		case VICTORY: tv.setText(R.string.victory);
		case DEFEAT: tv.setText(R.string.defeat);
		case STALEMATE: tv.setText(R.string.stalemate);
		}		
	}
}